package cody;

import com.google.wave.api.Range;
import com.google.wave.api.TextView;

public final class TextProcessor {
  // string constants
  private final static String TAG_CODE_OPEN = "<c>";
  private final static String TAG_CODE_CLOSE = "</c>";
  private final static String SEP_CODE = " \n\n";

  // wave syntax constants
  private final static String ANNOTATION_COLOR = "style/color";
  private final static String ANNOTATION_FONT_STYLE = "style/fontStyle";
  private final static String ANNOTATION_FONT_WEIGHT = "style/fontWeight";
  private final static String VALUE_BOLD = "bold";
  private final static String VALUE_ITALIC = "italic";
  private final static String VALUE_NORMAL = "normal";

  // functional parameters
  private final static String COLOR_CODE = "rgb(0,100,0)";
  private final static String COLOR_STRING = "rgb(3,17,255)";
  private final static String COLOR_ANNOTATION = "rgb(128,128,128)";
  

  /**
   * Function that changes the code between <c> and </c>
   * 
   * @param tv
   *          The TextView to be changed
   */
  public final static void process(TextView tv) {
    String text = tv.getText();
    final int end = text.lastIndexOf(TAG_CODE_CLOSE);
    if (end == -1) {
      return;
    }
    final int start = text.indexOf(TAG_CODE_OPEN);
    if (start == -1) {
      return;
    }

    // adding new lines at the beginning and end, instead of the tags
    tv.replace(new Range(start, start + TAG_CODE_OPEN.length()), SEP_CODE);
    tv.replace(new Range(end, end + TAG_CODE_CLOSE.length()), SEP_CODE + " ");

    // delimiting code
    Range r = new Range(start, end);

    // indentation
    String code = tv.getText(r).replace("\n", "\n  ");
    tv.replace(r, code);

    r = new Range(start + SEP_CODE.length(), start + SEP_CODE.length()
        + code.length());

    // setting color of code
    tv.setAnnotation(r, ANNOTATION_COLOR, COLOR_CODE);

    // setting bold weight
    tv.setAnnotation(r, ANNOTATION_FONT_WEIGHT, VALUE_BOLD);

    // setting monospace font
    tv.setAnnotation(r, "style/fontFamily", "Courier New");

    // looking for any comments and marking them out
    TextProcessor.processSyntax(tv, code, r);

  }

  /**
   * Processes the content of the code
   * 
   * @param tv
   *          The TextView which contains the code
   * @param code
   *          The code as a string
   * @param r
   *          The range of the code in the TextView
   */
  private final static void processSyntax(TextView tv, String code, Range r) {
    final int codeStart = r.getStart() - 3; // TODO correct this -3 correction
    // :) Wave's fault
    int state = 0, i = 0, start = -1, end = -1;
    while (i < code.length()) {
      switch (state) {
      case 0:
        switch (code.charAt(i)) {
        case '/':
          state = 1;
          break;
        case '"':
          state = 5;
          start = i; // memorizing the beginning of the string
          break;
        case '\'':
          state = 6;
          start = i; // memorizing the beginning of the string
          break;
        case '<':
          state = 7;
          break;
        case '@':
          state=13; 
          start = i; // memorizing the beginning of the Java annotation
          break;
        }
        break;
      case 1:
        switch (code.charAt(i)) {
        case '/':
          state = 2; // we have a comment? '//'
          start = i - 1; // memorizing the beginning of the comment
          break;
        case '*':
          state = 3; // we have a comment? '/*'
          start = i - 1; // memorizing the beginning of the comment
          break;
        default:
          state = 0;
        }
        break;
      case 2:
        switch (code.charAt(i)) {
        case '\n':
          state = 0; // comment is over
          end = i; // memorizing the end of the comment
          tv.setAnnotation(new Range(codeStart + start, codeStart + end),
              ANNOTATION_FONT_WEIGHT, VALUE_NORMAL);
          break;
        }
        break;
      case 3:
        switch (code.charAt(i)) {
        case '*':
          state = 4; // this might be part of '*/' but we have to make sure in
          // state 4
          break;
        }
        break;
      case 4:
        switch (code.charAt(i)) {
        case '/':
          state = 0; // comment is over
          end = i + 1; // memorizing the end of the comment
          tv.setAnnotation(new Range(codeStart + start, codeStart + end),
              ANNOTATION_FONT_WEIGHT, VALUE_NORMAL);
          break;
        }
        break;
      case 5:
        switch (code.charAt(i)) {
        case '"':
          state = 0;
          end = i + 1;
          Range stringRange = new Range(codeStart + start, codeStart + end);
          tv.setAnnotation(stringRange, ANNOTATION_FONT_STYLE, VALUE_ITALIC);
          tv.setAnnotation(stringRange, ANNOTATION_COLOR, COLOR_STRING);
          break;
        }
        break;
      case 6:
        switch (code.charAt(i)) {
        case '\'':
          state = 0;
          end = i + 1;
          Range stringRange = new Range(codeStart + start, codeStart + end);
          tv.setAnnotation(stringRange, ANNOTATION_FONT_STYLE, VALUE_ITALIC);
          tv.setAnnotation(stringRange, ANNOTATION_COLOR, COLOR_STRING);
          break;
        }
        break;
      case 7:
        switch (code.charAt(i)) {
        case '!':
          state = 8; // we might have an XML comment: '<!-- bla -->'
          break;
        default:
          state = 0;
        }
        break;
      case 8:
        switch (code.charAt(i)) {
        case '-':
          state = 9;
          break;
        default:
          state = 0;
        }
        break;
      case 9:
        switch (code.charAt(i)) {
        case '-':
          state = 10;
          start = i - 3; // the start of an XML comment: '<!-- bla -->'
          break;
        default:
          state = 0;
        }
        break;
      case 10:
        switch (code.charAt(i)) {
        case '-':
          state = 11; // the XML comment might end
          break;
        }
        break;
      case 11:
        switch (code.charAt(i)) {
        case '-':
          state = 12; // the XML comment might end
          break;
        }
        break;
      case 12:
        switch (code.charAt(i)) {
        case '>':
          state = 0; // the XML has ended
          end = i + 1;
          tv.setAnnotation(new Range(codeStart + start, codeStart + end),
              ANNOTATION_FONT_WEIGHT, VALUE_NORMAL);
          break;
        }
        break;
      case 13:
        switch (code.charAt(i)) {
        case '\n':
          state = 0; // the Java annotation has ended
          end = i;
          tv.setAnnotation(new Range(codeStart + start, codeStart + end),
              ANNOTATION_COLOR, COLOR_ANNOTATION);
          break;
        }
        break;
      }
      i++;
    }
  }
}
